#include <cstdio>
#include <cstring>
#include <vector>
#include "clang-c/CXString.h"
#include <clang-c/CXErrorCode.h>
#include <clang-c/Index.h>
#include "utils.hpp"
#include <iostream>

#include "parser/meta_parser.hpp"

#if _MSC_VER
#define strdup _strdup
#endif

std::ostream& operator<<(std::ostream& stream, const CXString& str)
{
  stream << clang_getCString(str);
  clang_disposeString(str);
  return stream;
}

/*
参数信息：
0： 程序本身
1： 要分析的源文件
2： 代码生成结果文件
3： 包含目录列表
4： 宏定义项
5： PCH文件
*/
int main(int argc, char **argv) {
    if (argc != 6)
        return 1;

    // dump command line
    printf("%s %s %s \"%s\" \"%s\" \"%s\"\n", argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);

    // check if latest, TODO: Enable this when finish dev
    // if (CodeGen::get_file_mtime(argv[2]) > CodeGen::get_file_mtime(argv[1]))
        // return 0; // already latest

    // CXString version = clang_getClangVersion();
    // printf("clang version: %s\n", clang_getCString(version));
    // clang_disposeString(version);

    // temperary values
    char *includes = strdup(argv[3]);
    char *defines = strdup(argv[4]);
    char *pch_headers = strdup(argv[5]);

    std::vector<const char *> args;
    args.emplace_back("-Xclang");
    args.emplace_back("-std=c++2a"); // c++20

    // parse in c++ mode
    // args.emplace_back("-x");
    // args.emplace_back("c++");

    // parse includes
    {
        int const len = strlen(includes);
        char *const end = includes + len;
        char *start = includes;
        char *pos = strstr(start+2, "-I"); // argv[3] always have atleast -I characters
        while(pos) {
            *(pos-1) = 0;
            args.emplace_back(start);
            start = pos;
            pos = strstr(start+2, "-I");
        }
        if (start < end) {
            args.emplace_back(start);
        }
    }

    // parse defines
    {
        int const len = strlen(defines);
        char *const end = defines + len;
        char *start = defines;
        char *pos = strstr(start+2, "-D"); // argv[4] always have atleast -D characters
        while(pos) {
            *(pos-1) = 0;
            args.emplace_back(start);
            start = pos;
            pos = strstr(start+2, "-D");
        }
        if (start < end) {
            args.emplace_back(start);
        }

        // add parser definitions
        args.emplace_back("-D__CODEGEN__");
    }

    // precompiled headers
    {
        int const len = strlen(pch_headers);
        char *const end = pch_headers + len;
        char *start = pch_headers;
        char *pos = strstr(start+2, "-H"); // argv[5] always have atleast -H characters
        while(pos) {
            *(pos-1) = 0;
            args.emplace_back("-include");
            args.emplace_back(start+2);
            start = pos;
            pos = strstr(start+2, "-H");
        }
        if (start+2 < end) {
            args.emplace_back("-include");
            args.emplace_back(start+2);
        }
    }

    // print cmdline args
    // for (size_t i = 0; i<args.size(); ++i) {
    //     printf("%3d:%s\n", (int)i, args[i]);
    // }

    int code = 0;

    CXIndex index = clang_createIndex(0, 0);
    CXTranslationUnit unit{};
    int parseFlags = CXTranslationUnit_SkipFunctionBodies;
    CXErrorCode ec = clang_parseTranslationUnit2(index, argv[1], &args[0], (int)args.size(), nullptr, 0, parseFlags, &unit);
    // CXTranslationUnit unit = clang_createTranslationUnitFromSourceFile(index, argv[1], (int)args.size(), &args[0], 0, nullptr);
    if (ec == CXError_Success) {
        CXCursor cursor = clang_getTranslationUnitCursor(unit);

        meta_parser parser(argv[1]);

        parser.prepare();
        if (parser.parser (cursor)) {
            if (parser.generate(argv[2]))
               parser.finish();
            else {
               code = 4;
            }
        } else {
            code = 3;
            fprintf (stderr, "meta parse failed!");
        }

        clang_disposeTranslationUnit(unit);
    } else {
        code = 2;
        fprintf(stderr, "Failed to parse file \"%s\", code %d\n", argv[1], ec);
    }

    clang_disposeIndex(index);

    free(includes);
    free(defines);

    return code;
}
